module spectrum_convolve use iso_fortran_env use fftpack implicit none private public :: convolve public :: SPCTRM_FULL_CONVOLUTION public :: SPCTRM_CENTRAL_CONVOLUTION integer(int32), parameter :: SPCTRM_FULL_CONVOLUTION = 50000 !! A flag for requesting a full convolution. integer(int32), parameter :: SPCTRM_CENTRAL_CONVOLUTION = 50001 !! A flag for requesting the central portion of the convolution that is !! the same length as the input signal. contains ! ------------------------------------------------------------------------------ pure function convolve(x, y, method) result(rst) !! Computes the convolution of a signal and kernel. real(real64), intent(in) :: x(:) !! The N-element signal. real(real64), intent(in) :: y(:) !! The M-element kernel. integer(int32), intent(in), optional :: method !! An optional input that dictates the expected !! convolution result. The following options are available. !! !! - SPCTRM_FULL_CONVOLUTION: The full convolution results are provided, !! including the portions polluted courtesy of the zero-padding and !! the corresponding wrap-around effects. The length of this output !! is N + M - 1. !! !! SPCTRM_CENTRAL_CONVOLUTION: The N-element result containing the !! convolved signal not poluted by the zero-padding and !! corresponding wrap-around effects. real(real64), allocatable :: rst(:) !! The convolved result. ! Local Variables integer(int32) :: mth, n1, n2 real(real64), allocatable :: temp(:) complex(real64), allocatable :: xmod(:), ymod(:) ! Initialization if (present(method)) then mth = method else mth = SPCTRM_FULL_CONVOLUTION end if ! Input Checking if (mth /= SPCTRM_FULL_CONVOLUTION .and. & mth /= SPCTRM_CENTRAL_CONVOLUTION) & then ! Reset mth = SPCTRM_FULL_CONVOLUTION end if ! Prepare the signals call prepare_conv(x, y, xmod, ymod, n1, n2) ! Compute the convolution if (mth == SPCTRM_CENTRAL_CONVOLUTION) then call convolve_driver(xmod, ymod, 1, temp) rst = temp(n1:n2) else call convolve_driver(xmod, ymod, 1, rst) end if end function ! ------------------------------------------------------------------------------ ! Prepares two signals for convolution. ! ! - x: The original signal of length N. ! - y: The original response signal of length M where M <= N. ! - xmod: The padded X signal of length M + N - 1. ! - ymod: The padded Y signal of length M + N - 1. ! - n2: The ending index of the unspoiled data pure subroutine prepare_conv(x, y, xmod, ymod, n1, n2) ! Arguments real(real64), intent(in) :: x(:), y(:) complex(real64), intent(out), allocatable :: xmod(:), ymod(:) integer(int32), intent(out) :: n1, n2 ! Parameters complex(real64), parameter :: zero = (0.0d0, 0.0d0) ! Local Variables integer(int32) :: nx, ny, n, nbig, dn, m logical :: evenY character(len = :), allocatable :: errmsg ! Initialization nx = size(x) ny = size(y) n = nx + ny - 1 ! Memory Allocation allocate(xmod(n), source = zero) allocate(ymod(n), source = zero) ! Copy over X & Y if (nx >= ny) then xmod(1:nx) = cmplx(x, 0.0d0, real64) ymod(1:ny) = cmplx(y, 0.0d0, real64) nbig = nx else xmod(1:ny) = cmplx(y, 0.0d0, real64) ymod(1:nx) = cmplx(x, 0.0d0, real64) nbig = ny end if ! Locate the unspoiled data points dn = n - nbig if (mod(dn, 2) == 0) then n1 = dn / 2 + 1 else n1 = (dn + 1) / 2 + 1 end if n2 = n1 + nbig - 1 end subroutine ! ------------------------------------------------------------------------------ ! Computes the convolution or deconvolution of two signals. ! ! - x: The zero-padded original signal. ! - y: The padded and wrapped response signal. ! - method: +1 for convolution, -1 for deconvolution. ! - rst: The convolved signal. pure subroutine convolve_driver(x, y, method, rst) ! Arguments complex(real64), intent(inout) :: x(:), y(:) integer(int32), intent(in) :: method real(real64), intent(out), allocatable :: rst(:) ! Local Variables integer(int32) :: i, n, lwork, nend real(real64) :: fac real(real64), allocatable, dimension(:) :: work complex(real64), allocatable, dimension(:) :: cxy character(len = :), allocatable :: errmsg ! Generate the workspace array for the transforms & allocate memory n = size(x) lwork = 4 * n + 15 allocate(work(lwork)) allocate(rst(n)) ! Compute the FFT's of both signals call zffti(n, work) call zfftf(n, x, work) call zfftf(n, y, work) ! Compute the scaling factor fac = 1.0d0 / real(n, real64) ! Perform the convolution operation if (method == 1) then ! Multiply the FFT's to convolve cxy = x * y else ! Divide the FFT's to deconvolve cxy = x / y end if ! Compute the inverse transform call zfftb(n, cxy, work) if (method == 1) then rst = real(fac * cxy) else rst = real(cxy) end if end subroutine ! ------------------------------------------------------------------------------ end module